//! NTFS filesystem-specific detection and optimization
//!
//! This module handles NTFS volume detection and provides NTFS-specific
//! optimizations like hardlink and junction point handling.

use crate::volume::{error::VolumeResult, types::Volume};
use async_trait::async_trait;
use std::path::{Path, PathBuf};
use tokio::task;
use tracing::{debug, warn};

/// NTFS filesystem handler
pub struct NtfsHandler;

impl NtfsHandler {
    pub fn new() -> Self {
        Self
    }

    /// Check if two paths are on the same NTFS volume
    pub async fn same_physical_storage(&self, path1: &Path, path2: &Path) -> bool {
        // Check if both paths are on the same NTFS volume
        if let (Ok(vol1), Ok(vol2)) = (
            self.get_volume_info(path1).await,
            self.get_volume_info(path2).await,
        ) {
            // Same volume GUID = same physical storage
            return vol1.volume_guid == vol2.volume_guid;
        }

        false
    }

    /// Get NTFS volume information for a path
    async fn get_volume_info(&self, path: &Path) -> VolumeResult<NtfsVolumeInfo> {
        let path = path.to_path_buf();

        task::spawn_blocking(move || {
            // Use PowerShell to get volume information
            let script = format!(
                r#"
                $volume = Get-Volume -FilePath '{}'
                $partition = Get-Partition -DriveLetter $volume.DriveLetter
                $disk = Get-Disk -Number $partition.DiskNumber

                [PSCustomObject]@{{
                    VolumeGuid = $volume.UniqueId
                    FileSystem = $volume.FileSystem
                    DriveLetter = $volume.DriveLetter
                    Label = $volume.FileSystemLabel
                    Size = $volume.Size
                    SizeRemaining = $volume.SizeRemaining
                    DiskNumber = $partition.DiskNumber
                    PartitionNumber = $partition.PartitionNumber
                    MediaType = $disk.MediaType
                }} | ConvertTo-Json
                "#,
                path.display()
            );

            let output = std::process::Command::new("powershell")
                .args(["-Command", &script])
                .output()
                .map_err(|e| {
                    crate::volume::error::VolumeError::platform(format!(
                        "Failed to run PowerShell: {}",
                        e
                    ))
                })?;

            if !output.status.success() {
                return Err(crate::volume::error::VolumeError::platform(
                    "PowerShell command failed".to_string(),
                ));
            }

            let output_text = String::from_utf8_lossy(&output.stdout);
            parse_volume_info(&output_text)
        })
        .await
        .map_err(|e| {
            crate::volume::error::VolumeError::platform(format!("Task join error: {}", e))
        })?
    }

    /// Check if NTFS hardlinks are supported (they always are on NTFS)
    pub async fn supports_hardlinks(&self, path: &Path) -> bool {
        // NTFS always supports hardlinks
        if let Ok(vol_info) = self.get_volume_info(path).await {
            return vol_info.file_system == "NTFS";
        }
        false
    }

    /// Check if NTFS junction points are supported
    pub async fn supports_junctions(&self, path: &Path) -> bool {
        // NTFS supports junction points (directory symbolic links)
        if let Ok(vol_info) = self.get_volume_info(path).await {
            return vol_info.file_system == "NTFS";
        }
        false
    }

    /// Resolve junction points and symbolic links
    pub async fn resolve_ntfs_path(&self, path: &Path) -> PathBuf {
        let path = path.to_path_buf();
        // Clone the path so we have an owned copy to move into the closure
        // while keeping the original 'path' available for the fallback (unwrap_or)
        let path_clone = path.clone();

        let result = task::spawn_blocking(move || {
            // Use the cloned path inside the closure
            let path = path_clone;
            
            // Use PowerShell to resolve the path
            let script = format!(
                r#"
                try {{
                    $resolvedPath = Resolve-Path -Path '{}' -ErrorAction Stop
                    Write-Output $resolvedPath.Path
                }} catch {{
                    Write-Output '{}'
                }}
                "#,
                path.display(),
                path.display()
            );

            let output = std::process::Command::new("powershell")
                .args(["-Command", &script])
                .output();

            match output {
                Ok(output) if output.status.success() => {
                    let resolved = String::from_utf8_lossy(&output.stdout).trim().to_string();
                    if !resolved.is_empty() {
                        PathBuf::from(resolved)
                    } else {
                        path
                    }
                }
                _ => path,
            }
        })
        .await;

        // If the task fails (e.g. panic), return the original path
        result.unwrap_or(path)
    }

    /// Get NTFS file system features
    pub async fn get_ntfs_features(&self, path: &Path) -> VolumeResult<NtfsFeatures> {
        let path = path.to_path_buf();

        task::spawn_blocking(move || {
            // Use fsutil to get NTFS features
            let script = format!(
                r#"
                $driveLetter = Split-Path -Path '{}' -Qualifier
                $features = @{{}}

                # Check for compression support
                try {{
                    $compressionInfo = fsutil behavior query DisableCompression 2>$null
                    $features.SupportsCompression = $true
                }} catch {{
                    $features.SupportsCompression = $false
                }}

                # Check for encryption support
                try {{
                    $encryptionInfo = fsutil behavior query DisableEncryption 2>$null
                    $features.SupportsEncryption = $true
                }} catch {{
                    $features.SupportsEncryption = $false
                }}

                # NTFS always supports these
                $features.SupportsHardlinks = $true
                $features.SupportsJunctions = $true
                $features.SupportsSymlinks = $true
                $features.SupportsStreams = $true

                $features | ConvertTo-Json
                "#,
                path.display()
            );

            let output = std::process::Command::new("powershell")
                .args(["-Command", &script])
                .output()
                .map_err(|e| {
                    crate::volume::error::VolumeError::platform(format!(
                        "Failed to run PowerShell: {}",
                        e
                    ))
                })?;

            if !output.status.success() {
                // Return default NTFS features
                return Ok(NtfsFeatures {
                    supports_hardlinks: true,
                    supports_junctions: true,
                    supports_symlinks: true,
                    supports_streams: true,
                    supports_compression: true,
                    supports_encryption: true,
                });
            }

            let output_text = String::from_utf8_lossy(&output.stdout);
            parse_ntfs_features(&output_text)
        })
        .await
        .map_err(|e| {
            crate::volume::error::VolumeError::platform(format!("Task join error: {}", e))
        })?
    }
}

#[async_trait]
impl super::FilesystemHandler for NtfsHandler {
    async fn enhance_volume(&self, volume: &mut Volume) -> VolumeResult<()> {
        // Add NTFS-specific information like feature support
        if let Some(mount_point) = volume.mount_point.to_str() {
            if let Ok(features) = self.get_ntfs_features(Path::new(mount_point)).await {
                debug!("Enhanced NTFS volume with features: {:?}", features);
                // Could store NTFS features in volume metadata
            }
        }
        Ok(())
    }

    async fn same_physical_storage(&self, path1: &Path, path2: &Path) -> bool {
        self.same_physical_storage(path1, path2).await
    }

    fn get_copy_strategy(&self) -> Box<dyn crate::ops::files::copy::strategy::CopyStrategy> {
        // Use streaming copy for NTFS (no built-in CoW like APFS/ReFS)
        // Could potentially use hardlinks for same-volume copies
        Box::new(crate::ops::files::copy::strategy::LocalStreamCopyStrategy)
    }

    fn contains_path(&self, volume: &Volume, path: &std::path::Path) -> bool {
        // Check primary mount point
        if path.starts_with(&volume.mount_point) {
            return true;
        }

        // Check additional mount points
        if volume.mount_points.iter().any(|mp| path.starts_with(mp)) {
            return true;
        }

        // TODO: NTFS-specific logic for junction points and mount points
        // Windows can have volumes mounted as folders (mount points) within other volumes
        // NTFS also supports junction points and symbolic links that may need resolution

        false
    }
}

/// NTFS volume information
#[derive(Debug, Clone)]
pub struct NtfsVolumeInfo {
    pub volume_guid: String,
    pub file_system: String,
    pub drive_letter: Option<char>,
    pub label: Option<String>,
    pub size_bytes: u64,
    pub available_bytes: u64,
    pub disk_number: Option<u32>,
    pub partition_number: Option<u32>,
    pub media_type: Option<String>,
}

/// NTFS filesystem features
#[derive(Debug, Clone)]
pub struct NtfsFeatures {
    pub supports_hardlinks: bool,
    pub supports_junctions: bool,
    pub supports_symlinks: bool,
    pub supports_streams: bool,
    pub supports_compression: bool,
    pub supports_encryption: bool,
}

/// Parse PowerShell volume info JSON output
fn parse_volume_info(json_output: &str) -> VolumeResult<NtfsVolumeInfo> {
    // Simple JSON parsing - in production, you'd use serde_json
    let json_output = json_output.trim();

    let volume_guid = extract_json_string(json_output, "VolumeGuid").unwrap_or_default();
    let file_system = extract_json_string(json_output, "FileSystem").unwrap_or_default();
    let drive_letter_str = extract_json_string(json_output, "DriveLetter");
    let label = extract_json_string(json_output, "Label");
    let size_bytes = extract_json_number(json_output, "Size").unwrap_or(0);
    let available_bytes = extract_json_number(json_output, "SizeRemaining").unwrap_or(0);
    let disk_number = extract_json_number(json_output, "DiskNumber").map(|n| n as u32);
    let partition_number = extract_json_number(json_output, "PartitionNumber").map(|n| n as u32);
    let media_type = extract_json_string(json_output, "MediaType");

    let drive_letter = drive_letter_str.and_then(|s| s.chars().next());

    Ok(NtfsVolumeInfo {
        volume_guid,
        file_system,
        drive_letter,
        label,
        size_bytes,
        available_bytes,
        disk_number,
        partition_number,
        media_type,
    })
}

/// Parse NTFS features JSON output
fn parse_ntfs_features(json_output: &str) -> VolumeResult<NtfsFeatures> {
    // Simple parsing - in production, use proper JSON parser
    let json_output = json_output.trim();

    let supports_compression =
        extract_json_bool(json_output, "SupportsCompression").unwrap_or(true);
    let supports_encryption = extract_json_bool(json_output, "SupportsEncryption").unwrap_or(true);

    Ok(NtfsFeatures {
        supports_hardlinks: true, // NTFS always supports these
        supports_junctions: true,
        supports_symlinks: true,
        supports_streams: true,
        supports_compression,
        supports_encryption,
    })
}

/// Extract string value from JSON (simple implementation)
fn extract_json_string(json: &str, key: &str) -> Option<String> {
    let pattern = format!("\"{}\":", key);
    if let Some(start) = json.find(&pattern) {
        let start = start + pattern.len();
        if let Some(value_start) = json[start..].find('"') {
            let value_start = start + value_start + 1;
            if let Some(value_end) = json[value_start..].find('"') {
                let value = &json[value_start..value_start + value_end];
                if value != "null" && !value.is_empty() {
                    return Some(value.to_string());
                }
            }
        }
    }
    None
}

/// Extract number value from JSON (simple implementation)
fn extract_json_number(json: &str, key: &str) -> Option<u64> {
    let pattern = format!("\"{}\":", key);
    if let Some(start) = json.find(&pattern) {
        let start = start + pattern.len();
        let remaining = json[start..].trim_start();
        if let Some(end) = remaining.find(|c: char| !c.is_ascii_digit()) {
            let number_str = &remaining[..end];
            return number_str.parse().ok();
        }
    }
    None
}

/// Extract boolean value from JSON (simple implementation)
fn extract_json_bool(json: &str, key: &str) -> Option<bool> {
    let pattern = format!("\"{}\":", key);
    if let Some(start) = json.find(&pattern) {
        let start = start + pattern.len();
        let remaining = json[start..].trim_start();
        if remaining.starts_with("true") {
            return Some(true);
        } else if remaining.starts_with("false") {
            return Some(false);
        }
    }
    None
}

/// Enhance volume with NTFS-specific information from Windows
pub async fn enhance_volume_from_windows(volume: &mut Volume) -> VolumeResult<()> {
    // FIX: Import the trait from the correct module
    use crate::volume::fs::FilesystemHandler;

    let handler = NtfsHandler::new();
    handler.enhance_volume(volume).await
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extract_json_string() {
        let json =
            r#"{"VolumeGuid": "12345678-1234-1234-1234-123456789abc", "FileSystem": "NTFS"}"#;
        assert_eq!(
            extract_json_string(json, "VolumeGuid"),
            Some("12345678-1234-1234-1234-123456789abc".to_string())
        );
        assert_eq!(
            extract_json_string(json, "FileSystem"),
            Some("NTFS".to_string())
        );
        assert_eq!(extract_json_string(json, "NonExistent"), None);
    }

    #[test]
    fn test_extract_json_bool() {
        let json = r#"{"SupportsCompression": true, "SupportsEncryption": false}"#;
        assert_eq!(extract_json_bool(json, "SupportsCompression"), Some(true));
        assert_eq!(extract_json_bool(json, "SupportsEncryption"), Some(false));
        assert_eq!(extract_json_bool(json, "NonExistent"), None);
    }
}